/*
 * Wazuh Vulnerability scanner - Scan Orchestrator
 * Copyright (C) 2015, Wazuh Inc.
 * May 6, 2024.
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation.
 */

#ifndef _CVE_SOLVED_INVENTORY_SYNC_HPP
#define _CVE_SOLVED_INVENTORY_SYNC_HPP

#include "chainOfResponsability.hpp"
#include "inventorySync.hpp"
#include "scanContext.hpp"

/**
 * @brief Class in charge of synchronizing the agent's inventory with the CVEs solved.
 *
 * @param TScanContext Scan context.
 */
template<typename TScanContext = ScanContext>
class TCVESolvedInventorySync final
    : public AbstractHandler<std::shared_ptr<TScanContext>>
    , public InventorySync
{

public:
    // LCOV_EXCL_START
    /**
     * @brief Construct a new TCVESolvedInventorySync object.
     *
     * @param inventoryDatabase Inventory database.
     */
    explicit TCVESolvedInventorySync(Utils::RocksDBWrapper& inventoryDatabase)
        : InventorySync(inventoryDatabase)
    {
    }
    // LCOV_EXCL_STOP

    /**
     * @brief Handles request and passes control to the next step of the chain.
     *
     * @param data Scan context.
     * @return std::shared_ptr<ScanContext> Abstract handler.
     */
    std::shared_ptr<TScanContext> handleRequest(std::shared_ptr<TScanContext> data) override
    {
        // We look for the CVEs in the agent's vulnerabilities inventory
        const auto& column = AFFECTED_COMPONENT_COLUMNS.at(AffectedComponentType::Package);

        for (const auto& [key, value] : TInventorySync<TScanContext>::m_inventoryDatabase.seek(data->agentId(), column))
        {
            auto inventory = Utils::split(value.ToString(), ',');
            auto currentSize = inventory.size();
            for (auto& [cve, json] : data->m_elements)
            {
                inventory.erase(
                    std::remove_if(inventory.begin(),
                                   inventory.end(),
                                   [&](const std::string& item)
                                   {
                                       if (item == cve)
                                       {
                                           std::string elementKey;
                                           elementKey.append(key);
                                           elementKey.append("_");
                                           elementKey.append(cve);

                                           logDebug2(WM_VULNSCAN_LOGTAG,
                                                     "CVE '%s' was remediated by hotfix '%s' for '%s'.",
                                                     cve.c_str(),
                                                     data->hotfixId().data(),
                                                     elementKey.c_str());
                                           json.push_back(std::move(
                                               TInventorySync<TScanContext>::buildElement("DELETED", elementKey)));

                                           return true;
                                       }

                                       return false;
                                   }),
                    inventory.end());
            }

            if (inventory.empty())
            {
                logDebug2(WM_VULNSCAN_LOGTAG, "Deleting agent element key: %s", key.c_str());
                m_inventoryDatabase.delete_(key, column);
            }
            else if (inventory.size() != currentSize)
            {
                std::string insertListString;
                for (const auto& cve : inventory)
                {
                    insertListString.append(cve);
                    insertListString.append(",");
                }
                insertListString.pop_back();
                logDebug2(
                    WM_VULNSCAN_LOGTAG, "Updating agent element key: %s -> %s", key.c_str(), insertListString.c_str());
                m_inventoryDatabase.put(key, insertListString, column);
            }
        }

        return AbstractHandler<std::shared_ptr<TScanContext>>::handleRequest(std::move(data));
    }
};

using CVESolvedInventorySync = TCVESolvedInventorySync<>;

#endif // _CVE_SOLVED_INVENTORY_SYNC_HPP
